package codeine.nodes;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

import javax.inject.Inject;

import org.apache.log4j.Logger;

import codeine.MongoPeerStatusUpdater;
import codeine.ProjectStatusUpdater;
import codeine.RunMonitors;
import codeine.SnoozeKeeper;
import codeine.configuration.ConfigurationManager;
import codeine.configuration.PathHelper;
import codeine.executer.PeriodicExecuter;
import codeine.executer.Task;
import codeine.jsons.nodes.NodeJson;
import codeine.jsons.nodes.NodesManager;
import codeine.jsons.project.ProjectJson;
import codeine.mail.MailSender;
import codeine.mail.NotificationDeliverToMongo;
import codeine.utils.InetUtils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public class NodesRunner implements Task{

	private static final Logger log = Logger.getLogger(NodesRunner.class);
	
	private static final long NODE_MONITOR_INTERVAL = TimeUnit.SECONDS.toMillis(29);
	public static final long NODE_RUNNER_INTERVAL = TimeUnit.HOURS.toMillis(1);
	
	private String hostname = InetUtils.getLocalHost().getHostName();
	@Inject
	private ConfigurationManager configurationManager;
	@Inject
	private PathHelper pathHelper;
	@Inject
	private ProjectStatusUpdater projectStatusUpdater;
	@Inject
	private MailSender mailSender;
	@Inject
	private NotificationDeliverToMongo notificationDeliverToMongo;
	@Inject
	private NodesManager nodesManager;
	@Inject
	private SnoozeKeeper snoozeKeeper;

	private Map<String, Map<String, PeriodicExecuter>> executers = Maps.newHashMap();

	@Inject
	private MongoPeerStatusUpdater mongoPeerStatusUpdater;
	
	@Override
	public void run() {
		for (ProjectJson project : getProjects()) {
			startExecutorsForProject(project);
		}
	}

	private void startExecutorsForProject(ProjectJson project) {
		Map<String, PeriodicExecuter> newProjectExecutors = Maps.newHashMap();
		Map<String, PeriodicExecuter> oldProjectExecutors = executers.put(project.name(), newProjectExecutors);
		if (null == oldProjectExecutors){
			oldProjectExecutors = Maps.newHashMap();
		}
		try {
			List<NodeJson> nodes = getNodes(project);
			if (nodes.isEmpty()) {
				log.info("ignoring project " + project.name() + ". not configured to run on host " + hostname);
				return;
			}
			for (NodeJson nodeJson : nodes) {
				// if (node.disabled())
				// {
				// log.info("node is disabled " + node.name);
				// continue;
				// }
				PeriodicExecuter e = oldProjectExecutors.remove(nodeJson.name());
				if (e == null){
					e = startExecuter(project, nodeJson);
				}
				newProjectExecutors.put(nodeJson.name(), e);
			}
		} catch (Exception e) {
			log.error("failed to configure project " + project.name() + " ,will ignore project", e);
		}
		for (Entry<String, PeriodicExecuter> e : oldProjectExecutors.entrySet()) {
			log.info("stop monitoring node " + e.getKey() + " in project " + project.name());
			e.getValue().stopWhenPossible();
		}
	}

	private PeriodicExecuter startExecuter(ProjectJson project, NodeJson nodeJson) {
		log.info("Starting monitor thread for project " + project.name() + " node " + nodeJson);
		PeriodicExecuter periodicExecuter = new PeriodicExecuter(NODE_MONITOR_INTERVAL, new RunMonitors(project, projectStatusUpdater, mailSender, pathHelper,
				nodeJson, notificationDeliverToMongo, mongoPeerStatusUpdater, snoozeKeeper), "RunMonitors_" + project.name() + "_" + nodeJson.name());
		new Thread(periodicExecuter).start();
		return periodicExecuter;
	}

	private List<ProjectJson> getProjects() {
		return configurationManager.getConfiguredProjects();
	}

	private List<NodeJson> getNodes(ProjectJson project) {
		try {
			return nodesManager.nodesOf(project).nodes();
		} catch (Exception e) {
			log.warn("failed to get nodes for project " + project.name(), e);
		}
		return Lists.newArrayList();
	}

}
